﻿using System;
using BaseApplicationNamespace;
using BasePersistenceNamespace;

namespace BaseApplicationNamespace
{
    /// <summary>
    /// データストアへの永続化を伴う作業を行うスクリプトの基本クラスです。
    /// </summary>
    /// <remarks>
    /// <para>
    /// <see cref="CommitmentScript&lt;TOutputs, TErrors&gt;"/> クラスは、操作スクリプトの基本クラスの中で最も多く利用されるクラスです。
    /// </para>
    /// <para>
    /// <see cref="CommitmentErrors"/> には永続化エラーが定義されており、永続化エラーが発生した場合は作業スペースをクリアしてエンティティの再取得を行う必要があります。
    /// これは通常、<see cref="WorkFacade">作業ファサード</see>の責務であり、
    /// <see cref="WorkFacade">作業ファサード</see>はこのための仕組みを 
    /// <see cref="WorkFacade.CommitmentErrorsHandler"/> プロパティという形で派生クラスに提供しています。
    /// </para>
    /// </remarks>
    /// <typeparam name="TOutputs">出力の型。</typeparam>
    /// <typeparam name="TErrors">エラーの型。</typeparam>
    [Serializable]
    public abstract class CommitmentScript<TOutputs, TErrors> : WorkScript<TOutputs, TErrors>
        where TOutputs : CommitmentOutputs
        where TErrors : CommitmentErrors, new()
    {
        /// <summary>
        /// <see cref="CommitmentScript&lt;TOutputs, TErrors&gt;"/> クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <param name="workspace">作業スペース。</param>
        /// <exception cref="System.ArgumentNullException">workspace が null です。</exception>
        protected CommitmentScript(IWorkspace workspace)
            : base(workspace)
        {
        }


        /// <inheritdoc/>
        protected override sealed ExecutionResult ExecuteCore()
        {
            var workResult = ExecuteWork();
            if (workResult.IsError)
            {
                return workResult;
            }
            return ExecuteSave(workResult.Outputs);
        }

        /// <summary>
        /// 作業を実行します。
        /// </summary>
        /// <returns>
        /// <para>
        /// 実行結果。
        /// </para>
        /// <para>
        /// 作業が成功した場合は <typeparamref name="TOutputs"/> のインスタンスを元に生成された <see cref="OperationScript&lt;TOutputs, TErrors&gt;.ExecutionResult"/>、
        /// 作業が失敗した場合は <typeparamref name="TErrors"/> のインスタンスを元に生成された <see cref="OperationScript&lt;TOutputs, TErrors&gt;.ExecutionResult"/> を返します。
        /// </para>
        /// </returns>
        /// <exception cref="System.InvalidOperationException">操作の実行に失敗しました。</exception>
        protected abstract ExecutionResult ExecuteWork();

        /// <summary>
        /// データストアへの永続化を実行します。
        /// </summary>
        /// <param name="workOutputs">作業で得た出力。</param>
        /// <returns>
        /// <para>
        /// 実行結果。
        /// </para>
        /// <para>
        /// 永続化が成功した場合は <typeparamref name="TOutputs"/> のインスタンスを元に生成された <see cref="OperationScript&lt;TOutputs, TErrors&gt;.ExecutionResult"/>、
        /// 永続化が失敗した場合は <typeparamref name="TErrors"/> のインスタンスを元に生成された <see cref="OperationScript&lt;TOutputs, TErrors&gt;.ExecutionResult"/> を返します。
        /// </para>
        /// </returns>
        private ExecutionResult ExecuteSave(TOutputs workOutputs)
        {
            try
            {
                Workspace.PersistAll();
                return workOutputs;
            }
            catch (ConcurrencyException)
            {
                return new TErrors
                {
                    ConcurrentAccessOccurs = true
                };
            }
        }
    }
}
